#ifndef CODEGEN_H
#define CODEGEN_H

#include <map>
#include <fstream>
#include "lir.h"



const int pREP = 0xF3;

const int GvEv = 0x03;
const int EvGv = 0x01;
const int EvIv = 0x01;

const int opADD = 0x00;
const int opOR  = 0x08;
const int opADC = 0x10;
const int opSBB = 0x18;
const int opAND = 0x20;
const int opSUB = 0x28;
const int opXOR = 0x30;
const int opCMP = 0x38;
const int opINC = 0x40;
const int opDEC = 0x48;
const int opPUSH = 0x50;
const int opPOP = 0x58;

const int opPUSHAD = 0x60;
const int opPOPAD = 0x61;
const int opPUSHIv = 0x68;

const int opMOVSD = 0xA5;

const int opLEA = 0x8D;

const int opXCHGEvGv = 0x87;

const int opMOVIb = 0xB0;
const int opMOVIv = 0xB8;

const int opMOVEbGb = 0x88;
const int opMOVEvGv = 0x89;
const int opMOVGvEv = 0x8B;

const int opRETIw = 0xC2;
const int opRET = 0xC3;

const int opCALL = 0xE8;
const int opJMP  = 0xE9;


const int Group1EvIv = 0x81;
const int Group1EvIb = 0x83;
const int Group2EvIb = 0xC1;
const int Group2EvCL = 0xD3;
const int Group3v = 0xF7;
const int Group5 = 0xFF;

const int Group11 = 0xC6;


const int MOD_REG = 3;


const int DISP32 = 5;


const int eopADD  = 0x0;
const int eopOR   = 0x1;
const int eopADC  = 0x2;
const int eopSBB  = 0x3;
const int eopAND  = 0x4;
const int eopSUB  = 0x5;
const int eopXOR  = 0x6;
const int eopCMP  = 0x7;

const int eopSHL  = 0x4;
const int eopSHR  = 0x5;
const int eopSAR  = 0x7;

const int eopNOT  = 0x2;
const int eopNEG  = 0x3;

const int eopCALLN = 0x2;
const int eopJMPN  = 0x4;

const int eopPUSH  = 0x6;


const int op2CMOVcc = 0x40;
const int op2Jcc    = 0x80;

const int op2MOVZXb = 0xB6;
const int op2MOVZXw = 0xB7;
const int op2MOVSXb = 0xBE;
const int op2MOVSXw = 0xBF;

const int op2IMUL   = 0xAF;


class Fixup
{
private:
    std::map<Lir*,int> lirToPc;
    std::vector<Lir*> fixups;

public:
    void setPc(Lir *lir,int pc);
    int getPc(Lir *lir);

    void requireFixup(Lir *lir);
    void addLabel(Lir *lir,Label *label);
    Label* getLabel(Lir *l);
    void doFixup(std::ostream &os);
};

class X86Codegen
{
private:
    int pc;
    std::ofstream codeStream;

    Fixup fixup;

    void emit8(unsigned char d);
    void emit32(unsigned int d);
    unsigned char makeModRM(char mod,char reg,char rm);
    void genAdd(Lir *lir);
    void genSub(Lir *lir);
    void genMul(Lir *lir);
    void genDiv(Lir *lir);
    void genMove(Lir *lir);
    void genJmp(Lir *lir);
    void genPush(Lir *lir);
    void genPop(Lir *lir);
    void genSpillLoad(Lir *lir);
    void genSpillStore(Lir *lir);
    void genLoadConst(Lir *lir);
    void genOr(Lir *lir);

public:
    X86Codegen();

    void genCode(CFG *cfg);
};

#endif
